// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package zlib

import (
	"compress/flate"
	"encoding/binary"
	"fmt"
	"hash"
	"hash/adler32"
	"io"
)

// 这些常量是从flate包复制的，因此导入
// “compress/zlib”的代码也不必导入“compress/flate”。
const (
	NoCompression      = flate.NoCompression
	BestSpeed          = flate.BestSpeed
	BestCompression    = flate.BestCompression
	DefaultCompression = flate.DefaultCompression
	HuffmanOnly        = flate.HuffmanOnly
)

// 写入程序获取写入其中的数据，并将压缩后的
// 格式的数据写入底层写入程序（参见NewWriter）。
type Writer struct {
	w           io.Writer
	level       int
	dict        []byte
	compressor  *flate.Writer
	digest      hash.Hash32
	err         error
	scratch     [4]byte
	wroteHeader bool
}

// NewWriter创建一个新的作家。
// 对返回的写入程序的写入被压缩并写入w。
// 
// 完成后，调用方有责任关闭写入程序。
// 写入操作可能会被缓冲，直到关闭后才会刷新。
func NewWriter(w io.Writer) *Writer {
	z, _ := NewWriterLevelDict(w, DefaultCompression, nil)
	return z
}

// NewWriterLevel与NewWriter类似，但它指定了压缩级别，而不是假设默认压缩的
// 。压缩级别可以是DefaultCompression、NoCompression、HuffmanOnly或BestSpeed和BestCompression之间的任意整数值。
// 如果级别有效，返回的错误将为零。
func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
	return NewWriterLevelDict(w, level, nil)
}

// NewWriterLevel dict与NewWriterLevel类似，但指定了一个字典来压缩。这本字典可能是零。否则，在
// 写入程序关闭之前，不应修改其内容。
func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) {
	if level < HuffmanOnly || level > BestCompression {
		return nil, fmt.Errorf("zlib: invalid compression level: %d", level)
	}
	return &Writer{
		w:     w,
		level: level,
		dict:  dict,
	}, nil
}

// 重置清除写入程序z的状态，使其等同于NewWriterLevel或NewWriterLevel中的初始状态，但将
// 写入w。
func (z *Writer) Reset(w io.Writer) {
	z.w = w
	// z.level和z.dict保持不变。
	if z.compressor != nil {
		z.compressor.Reset(w)
	}
	if z.digest != nil {
		z.digest.Reset()
	}
	z.err = nil
	z.scratch = [4]byte{}
	z.wroteHeader = false
}

// writeHeader写入ZLIB头。
func (z *Writer) writeHeader() (err error) {
	z.wroteHeader = true
	// ZLIB有一个两字节的头（如RFC 1950中所述）。
	// 前四位是CINFO（压缩信息），默认的deflate窗口大小为7。
	// 接下来的四位是CM（压缩方法），8表示deflate。
	z.scratch[0] = 0x78
	// 接下来的两位是FLEVEL（压缩级别）。这四个值是：
	// 0=最快，1=最快，2=默认，3=最佳。
	// 如果给定字典，则设置下一位FDICT。
	// 最后五个FCHECK位构成mod-31校验和。
	switch z.level {
	case -2, 0, 1:
		z.scratch[1] = 0 << 6
	case 2, 3, 4, 5:
		z.scratch[1] = 1 << 6
	case 6, -1:
		z.scratch[1] = 2 << 6
	case 7, 8, 9:
		z.scratch[1] = 3 << 6
	default:
		panic("unreachable")
	}
	if z.dict != nil {
		z.scratch[1] |= 1 << 5
	}
	z.scratch[1] += uint8(31 - (uint16(z.scratch[0])<<8+uint16(z.scratch[1]))%31)
	if _, err = z.w.Write(z.scratch[0:2]); err != nil {
		return err
	}
	if z.dict != nil {
		// 接下来的四个字节是字典的Adler-32校验和。
		binary.BigEndian.PutUint32(z.scratch[:], adler32.Checksum(z.dict))
		if _, err = z.w.Write(z.scratch[0:4]); err != nil {
			return err
		}
	}
	if z.compressor == nil {
		// 初始化deflater，除非在重置调用后重新使用写入程序
		// 否则初始化deflater。
		z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict)
		if err != nil {
			return err
		}
		z.digest = adler32.New()
	}
	return nil
}

// Write将p的压缩形式写入底层io。作家在关闭写入程序或显式刷新
// 之前，不一定刷新压缩字节。
func (z *Writer) Write(p []byte) (n int, err error) {
	if !z.wroteHeader {
		z.err = z.writeHeader()
	}
	if z.err != nil {
		return 0, z.err
	}
	if len(p) == 0 {
		return 0, nil
	}
	n, err = z.compressor.Write(p)
	if err != nil {
		z.err = err
		return
	}
	z.digest.Write(p)
	return
}

// Flush将写入程序刷新到其底层io。作家
func (z *Writer) Flush() error {
	if !z.wroteHeader {
		z.err = z.writeHeader()
	}
	if z.err != nil {
		return z.err
	}
	z.err = z.compressor.Flush()
	return z.err
}

// Close关闭写入程序，将所有未写入的数据刷新到基础
// io。写入程序，但不关闭底层io。作家
func (z *Writer) Close() error {
	if !z.wroteHeader {
		z.err = z.writeHeader()
	}
	if z.err != nil {
		return z.err
	}
	z.err = z.compressor.Close()
	if z.err != nil {
		return z.err
	}
	checksum := z.digest.Sum32()
	// ZLIB（RFC 1950）是大端的，不同于GZIP（RFC 1952）。
	binary.BigEndian.PutUint32(z.scratch[:], checksum)
	_, z.err = z.w.Write(z.scratch[0:4])
	return z.err
}
